package com.tgh.gles2.c3.primitives;

import com.tgh.utils.GLShaderManager;
import com.tgh.utils.GLShaderManager.ShaderType;
import com.tgh.utils.GLTools;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;

import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MySurfaceView extends GLSurfaceView {

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private float lastX,lastY;
    private float xAngle,yAngle;
    private float factor=(float) (5*Math.PI/180);
    private boolean isClick;
    private int step;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x=event.getX();
        float y=event.getY();
        switch (event.getAction()&MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                lastX=x;
                lastY=y;
                isClick=true;
                return true;
            case MotionEvent.ACTION_UP:
                lastX=x;
                lastY=y;
                addStepIfNeed();
                return true;
            case MotionEvent.ACTION_MOVE:
                float dy=lastY-y;
                if (dy>10||dy<-10) {
                    xAngle+=dy*factor;
                    isClick=false;
                }
                float dx=x-lastX;
                if (dx>10 || dx<-10) {
                    yAngle+=dx*factor;
                    isClick=false;
                }
                requestRender();
                lastX=x;
                lastY=y;
                return true;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
    
    private void init() {
        setEGLContextClientVersion(2);
        setRenderer(new SceneRenderer());
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
    
    private void addStepIfNeed() {
        if (isClick) {
            step++;
            if (step>=7) {
                step=0;
            }
            requestRender();
        }
    }
    
    private class SceneRenderer implements Renderer{

        private float[] projectionMatrixs=new float[16];
        private float[] modelViewMatrix=new float[16];
        private float[] modelMatrix=new float[16];
        private float[] cameraMatrix=new float[16];
        private float[] mvpMatrix=new float[16];
        
        private float[] green={0,1,0,1};
        private float[] black={0,0,0,0.75f};
        
        private GLShaderManager shaderManager;
        private FloatBuffer triangleStrips;
        private FloatBuffer triangles;
        private FloatBuffer triangleFan;
        private FloatBuffer points;

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES20.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
            GLES20.glEnable(GLES20.GL_DEPTH_TEST);
            
            shaderManager = new GLShaderManager();
            shaderManager.initStockShader();
            initVertices();
            
            Matrix.setLookAtM(cameraMatrix, 0, 0, 0, -15, 0, 0, 0, 0, 1, 0);
            
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            float ratio=(float)width/(float)height;
            
            GLTools.setPerspective(projectionMatrixs,35f, ratio, 1, 500);
            
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
            
            Matrix.setRotateM(modelMatrix, 0, xAngle, 1, 0, 0);
            Matrix.rotateM(modelMatrix, 0, yAngle, 0, 1, 0);
            Matrix.multiplyMM(modelViewMatrix, 0, cameraMatrix, 0, modelMatrix, 0);
            Matrix.multiplyMM(mvpMatrix, 0, projectionMatrixs, 0, modelViewMatrix, 0);
            switch (step) {
                case 0:
                    drawPoints();
                    break;
                case 1:
                    drawLines(GLES20.GL_LINES);
                    break;
                case 2:
                    drawLines(GLES20.GL_LINE_STRIP);
                case 3:
                    drawLines(GLES20.GL_LINE_LOOP);
                    break;
                case 4:
                    drawWireFramedPolygon(GLES20.GL_TRIANGLES, triangles);
                    break;
                case 5:
                    drawWireFramedPolygon(GLES20.GL_TRIANGLE_STRIP, triangleStrips);
                    break;
                case 6:
                    drawWireFramedPolygon(GLES20.GL_TRIANGLE_FAN, triangleFan);
                    break;
                default:
                    break;
            }
        }
 
        private void initVertices() {
            initPoints();
            initTriangles();
            initTriangleStrips();
            initTriangleFan();
        }
        
        private void drawPoints() {
            GLES20.glVertexAttribPointer(GLShaderManager.GL_ATTRIBUTE_VERTEX, 3, GLES20.GL_FLOAT, false, 0, points);
            GLES20.glEnableVertexAttribArray(GLShaderManager.GL_ATTRIBUTE_VERTEX);
            
            
            shaderManager.useStockShader(ShaderType.FLAT, mvpMatrix,black,4f);
            GLES20.glDrawArrays(GLES20.GL_POINTS, 0, points.capacity()/3);
            shaderManager.useStockShader(ShaderType.FLAT, mvpMatrix,black,1f);
        }
        
        private void drawLines(int primitives) {
            GLES20.glVertexAttribPointer(GLShaderManager.GL_ATTRIBUTE_VERTEX, 3, GLES20.GL_FLOAT, false, 0, points);
            GLES20.glEnableVertexAttribArray(GLShaderManager.GL_ATTRIBUTE_VERTEX);
            
            shaderManager.useStockShader(ShaderType.FLAT, mvpMatrix,black);
            GLES20.glLineWidth(2f);
            GLES20.glDrawArrays(primitives, 0, points.capacity()/3);
            GLES20.glLineWidth(1f);
        }
        /**
         * 画带线框的多边形
         */
        private void drawWireFramedPolygon(int primitive,FloatBuffer vertices) {
            
            GLES20.glVertexAttribPointer(GLShaderManager.GL_ATTRIBUTE_VERTEX, 3, GLES20.GL_FLOAT, false, 0, vertices);
            GLES20.glEnableVertexAttribArray(GLShaderManager.GL_ATTRIBUTE_VERTEX);
            
            shaderManager.useStockShader(ShaderType.FLAT, mvpMatrix, green);
            
            GLES20.glDrawArrays(primitive, 0, vertices.capacity()/3);
            
            GLES20.glPolygonOffset(-1.0f, -1.0f);
            GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
            GLES20.glEnable(GLES20.GL_BLEND);
            GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
            GLES20.glLineWidth(2.5f);
            shaderManager.useStockShader(ShaderType.FLAT, mvpMatrix,black);
            GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, vertices.capacity()/3);
            
            GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
            GLES20.glDisable(GLES20.GL_BLEND);
            GLES20.glLineWidth(1.0f);
        }
        
        private void initPoints() {
            float[][] vPoints=new float[][]{
                    {2.80f, 1.20f, 0.0f }, {2.0f,  1.20f, 0.0f },
                    {2.0f,  1.08f, 0.0f },  {2.0f,  1.08f, 0.0f },
                    {0.0f,  0.80f, 0.0f},  {-.32f, 0.40f, 0.0f },
                    {-.48f, 0.2f, 0.0f },   {-.40f, 0.0f, 0.0f},
                    {-.60f, -.40f, 0.0f },  {-.80f, -.80f, 0.0f },
                    {-.80f, -1.4f, 0.0f},  {-.40f, -1.60f, 0.0f },
                    {0.0f, -1.20f, 0.0f },  { .2f, -.80f, 0.0f },
                    {.48f, -.40f, 0.0f },   {.52f, -.20f, 0.0f },
                    {.48f,  .20f, 0.0f },   {.80f,  .40f, 0.0f },
                    {1.20f, .80f, 0.0f },   {1.60f, .60f, 0.0f },
                    {2.0f, .60f, 0.0f },    {2.2f, .80f, 0.0f },
                    {2.40f, 1.0f, 0.0f },   {2.80f, 1.0f, 0.0f }
            };
            points = GLTools.makeFloatBuffer(vPoints);
        }
        
        private void initTriangles() {
            float[] vPoints=new float[]{
                    -2,0,2,
                    2,0,2,
                    0,4,0,
                    
                    -2,0,-2,
                    0,4,0,
                    2,0,-2,
                    
                    -2,0,-2,
                    -2,0,2,
                    0,4,0,
                    
                    2,0,-2,
                    2,0,2,
                    0,4,0
            };
            triangles = GLTools.makeFloatBuffer(vPoints);
        }
        
        private void initTriangleStrips() {
            float[][] temp=new float[100][3];
            int count=0;
            float radius=3.0f;
            for(float angle=0;angle<2*Math.PI;angle+=0.3f){
                float x=radius*FloatMath.sin(angle);
                float y=radius*FloatMath.cos(angle);
                
                temp[count][0]=x;
                temp[count][1]=y;
                temp[count][2]=-0.5f;
                count++;
                temp[count][0]=x;
                temp[count][1]=y;
                temp[count][2]=0.5f;
                count++;
            }
            temp[count][0]=temp[0][0];
            temp[count][1]=temp[0][1];
            temp[count][2]=-0.5f;
            count++;
            
            temp[count][0]=temp[1][0];
            temp[count][1]=temp[1][1];
            temp[count][2]=0.5f;
            count++;
            
            float[] vPoint=new float[count*3];
            for(int i=0;i<count;i++){
                vPoint[i*3+0]=temp[i][0];
                vPoint[i*3+1]=temp[i][1];
                vPoint[i*3+2]=temp[i][2];
            }
            triangleStrips = GLTools.makeFloatBuffer(vPoint);
        }
        
        private void initTriangleFan() {
            float[][] vPoints=new float[9][3];
            float radius=3f;
            int nVerts=0;
            vPoints[nVerts][0]=0.0f;
            vPoints[nVerts][1]=0.0f;
            vPoints[nVerts][2]=0.0f;
            float radian=(float) (2*Math.PI);
            for(float angle=0;angle<radian;angle+=radian/6f){
                nVerts++;
                vPoints[nVerts][0]=FloatMath.cos(angle)*radius;
                vPoints[nVerts][1]=FloatMath.sin(angle)*radius;
                vPoints[nVerts][2]=-0.5f;
            }
            nVerts++;
            vPoints[nVerts][0]=radius;
            vPoints[nVerts][1]=0.0f;
            vPoints[nVerts][2]=0.0f;
            triangleFan = GLTools.makeFloatBuffer(vPoints);
        }
    }
}
